using System.Collections.Generic;
using System.Data;
using Tycho.Compiler;
using Tycho.Runtime;

namespace Tycho.Parser {
    public class TermGeneratorBuilder {
        public delegate TermGenerator UnquoteExpressionDelegate (AnyObject expression);

        readonly AnyObject GeneratedModule;

        public TermGeneratorBuilder (AnyObject generatedModule) {
            GeneratedModule = generatedModule;
        }

        private bool IsUnquotedEtcSymbol (AnyObject term) {
            if (IsUnquoted (term)) {
                AnyObject expression = term.GetProperty (ExpressionCompiler.ExpressionSymbol);
                if (expression.GetProperty (Symbols.ParserTermName) == ExpressionCompiler.EtcSymbol) {
                    return true;
                }
            }

            return false;
        }

        private bool IsUnquoted (AnyObject term) {
            return term is StructureObject && term.GetProperty (Symbols.ParserTermName) == ExpressionCompiler.UnquoteSymbol;
        }

        public TermGenerator Build (AnyObject term, UnquoteExpressionDelegate unquoteExpression) {
            if (term is ListObject) {
                var list = new ListGenerator ();

                TermGenerator lastTermGen = null;

                foreach (var item in term) {
                    if (IsUnquotedEtcSymbol (item)) {
                        if (lastTermGen == null) {
                            throw new SyntaxErrorException("expected term to expand into list before #...");
                        }
                        list.AddVariableItems (lastTermGen);
                        lastTermGen = null;
                    } else {
                        if (lastTermGen != null) {
                            list.AddItem (lastTermGen);
                        }
                        lastTermGen = Build (item, unquoteExpression);
                    }
                }

                if (lastTermGen != null) {
                    list.AddItem (lastTermGen);
                }

                return list;
            } else if (term is StructureObject) {
                if (term.HasProperty (Symbols.ParserTermName)
                    && term.GetProperty (Symbols.ParserTermName) == ExpressionCompiler.UnquoteSymbol) {
                    return unquoteExpression (term.GetProperty (ExpressionCompiler.ExpressionSymbol));
                } else {
                    if (term.HasProperty (Symbols.ParserTermName)
                        && term.GetProperty (Symbols.ParserTermName) == Symbols.ParserIdentifier
                        && !term.HasProperty (Symbols.ParserModule)) {
                        term.SetProperty (Symbols.ParserModule, GeneratedModule);
                    }

                    var structure = new StructureGenerator (term.SourceLocation);

                    foreach (KeyValuePair<AnyObject, AnyObject> field in term.Expect<StructureObject> ().Fields) {
                        structure.Fields.Add (new StructureGenerator.Field () { Name = field.Key, Value = Build (field.Value, unquoteExpression) });
                    }

                    return structure;
                }
            } else {
                return new ConstantGenerator (term);
            }
        }
    }
}